package com.log4jviewer.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.log4jviewer.ErrorsContainer;
import com.log4jviewer.LogEventContainer;
import com.log4jviewer.LogEventListener;
import com.log4jviewer.domain.LogEvent;

/**
 * Class defines server which can process different clients.
 * 
 * @author <a href="mailto:rd.ryly@gmail.com">Ruslan Diachenko</a>
 */
public class Server extends Thread {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private ServerSocket serverSocket;

    private int port;

    private ErrorsContainer errorsContainer;

    private LogEventContainer logEventContainer;

    public Server(final int port, final ErrorsContainer errorsContainer) {
        this.port = port;
        this.errorsContainer = errorsContainer;
        logEventContainer = new LogEventContainer();
    }

    public void sendError(final String errorMessage) {
        errorsContainer.addError(errorMessage);
    }

    @Override
    public void run() {
        Socket socket = null;

        try {
            serverSocket = new ServerSocket(port);
            logger.debug("Server was started.");

            while (!serverSocket.isClosed()) {
                // Waiting for a new client.
                socket = serverSocket.accept();

                logger.debug("{}", socket.getLocalPort());
                ClientConnection clientConnection = new ClientConnection(this, socket);
                clientConnection.start();
            }

        } catch (SocketException e) {
            logger.warn("Server connection was closed!", e);

        } catch (IOException e) {
            String errorMessage = "Error occured when starting server!";
            sendError(errorMessage);
            logger.error(errorMessage, e);

        } finally {
            try {
                // close socket if it's open
                if ((socket != null) && !socket.isClosed()) {
                    socket.shutdownOutput();
                    socket.shutdownInput();
                    socket.close();
                    logger.info("Socket was closed!");
                }
                stopServer();

            } catch (IOException e) {
                String errorMessage = "Error occured when closing socket!";
                sendError(errorMessage);
                logger.error(errorMessage, e);
            }
        }
    }

    public void stopServer() {
        try {
            if (!serverSocket.isClosed()) {
                serverSocket.close();
            }
            logger.info("Server was stopped.");
        } catch (IOException e) {
            String errorMessage = "Error occured when stopping server!";
            sendError(errorMessage);
            logger.error(errorMessage, e);
        }
    }

    public void notifyListeners(final LogEvent logEvent) {
        logEventContainer.notifyListeners(logEvent);
    }

    public void addListener(final LogEventListener listener) {
        logEventContainer.addListener(listener);
    }

    public void removeListener(final LogEventListener listener) {
        logEventContainer.removeListener(listener);
    }
}
